#include "../../pch.h"
#include "REFXMLTextEncoder.h"
#include "XMLCharType.h"

namespace BFX
{

REFXMLTextEncoder::REFXMLTextEncoder(REFUniTextWriter* pWriter)
{
	BFX_REQUIRE (pWriter != NULL);
	m_bInAttribute = false;
	m_pWriter = pWriter;
}

REFXMLTextEncoder::~REFXMLTextEncoder()
{
}

void REFXMLTextEncoder::Write(UCHAR ch)
{
	m_pWriter->Write(ch);
}

void REFXMLTextEncoder::Write(const UniString& strText)
{
	if (strText.IsEmpty())
		return;

	// scan through the string to see if there are any characters to be escaped
	int nLen = strText.GetLength();
	int i = 0;
	int iStartPos = 0;
	UCHAR ch = 0;
	for (;;)
	{
		while (i < nLen && XMLCharType::IsAttributeValueChar(ch = strText[i]))
		{
			i ++;
		}
		if (i == nLen)
		{
			// reached the end of the string -> write it whole out
			m_pWriter->Write(strText);
			return;
		}
		if (m_bInAttribute)
		{
			if (ch == 0x09)
			{
				i++;
				continue;
			}
		}
		else
		{
			if (ch == 0x9 || ch == 0xA || ch == 0xD || ch == '"' || ch == '\'')
			{
				i++;
				continue;
			}
		}
		// some character that needs to be escaped is found:
		break;
	}

	for (;;)
	{
		if (iStartPos < i)
		{
			m_pWriter->Write(strText, iStartPos, i - iStartPos);
		}
		if (i == nLen)
		{
			break;
		}

		switch (ch)
		{
		case (char)0x9:
			m_pWriter->Write(ch);
			break;
		case (char)0xA:
		case (char)0xD:
			if (m_bInAttribute)
			{
				WriteCharEntityImpl(ch);
			}
			else
			{
				m_pWriter->Write(ch);
			}
			break;
		case '<':
			WriteEntityRefImpl(USTR("lt"));
			break;
		case '>':
			WriteEntityRefImpl(USTR("gt"));
			break;
		case '&':
			WriteEntityRefImpl(USTR("amp"));
			break;
		case '\'':
			if (m_bInAttribute && '"' == ch)
			{
				WriteEntityRefImpl(USTR("apos"));
			}
			else {
				m_pWriter->Write('\'');
			}
			break;
		case '"':
			if (m_bInAttribute && '"' == ch)
			{
				WriteEntityRefImpl(USTR("quot"));
			}
			else
			{
				m_pWriter->Write('"');
			}
			break;
		default:
			if ((int)ch >= W_HIGH_SURROGATE_START && (int)ch <= W_HIGH_SURROGATE_END)
			{
				BFX_ENSURE1 (i + 1 < nLen, "Invalid surrogate pair");
				WriteSurrogateChar(strText[++i], ch);
			}
			else
			{
				BFX_ENSURE1 (((int)ch < W_LOW_SURROGATE_START || (int)ch > W_LOW_SURROGATE_END), "Invalid high surrogate char");
				BFX_ASSERT(( ch < 0x20 && !XMLCharType::IsWhiteSpace(ch) ) || (ch > 0xFFFD));
				WriteCharEntityImpl(ch);
			}
			break;
		}
		i++;
		iStartPos = i;
		while (i < nLen && ( XMLCharType::IsAttributeValueChar((strText[i]))))
		{
			i++;
		}
	}
}

void REFXMLTextEncoder::StartAttribute()
{
	m_bInAttribute = true;
}

void REFXMLTextEncoder::EndAttribute()
{
	m_bInAttribute = false;
}

void REFXMLTextEncoder::WriteSurrogateChar(UCHAR lowChar, UCHAR highChar)
{
	// check for invalid surrogate pair.
	BFX_ENSURE ((int)lowChar  >= W_LOW_SURROGATE_START  && (int)lowChar  <= W_LOW_SURROGATE_END);
	BFX_ENSURE ((int)highChar >= W_HIGH_SURROGATE_START && (int)highChar <= W_HIGH_SURROGATE_END);

	m_pWriter->Write(highChar);
	m_pWriter->Write(lowChar);
}

void REFXMLTextEncoder::WriteEntityRefImpl(const UniString& str)
{
	m_pWriter->Write('&');
	m_pWriter->Write(str);
	m_pWriter->Write(';');
}

void REFXMLTextEncoder::WriteCharEntityImpl(UCHAR ch)
{
	static UCHAR HEX_TABLE[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

	if (ch > 0x00ff)
	{
		// high byte
		m_pWriter->Write(HEX_TABLE[(BYTE)(((int)ch & 0xf000) >> 12)]);
		m_pWriter->Write(HEX_TABLE[(BYTE)(((int)ch & 0x0f00) >> 8)]);
	}
	// low byte
	m_pWriter->Write(HEX_TABLE[(BYTE)(((int)ch & 0x00f0) >> 4)]);
	m_pWriter->Write(HEX_TABLE[(BYTE)((int)ch & 0x000f)]);
}

}	//	namespace BFX
